const min = Math.min;
const max = Math.max;
const abs = Math.abs;
const DEFAULT_TRIGGER = "shift";
const ALLOW_EVENTS = ["drag", "shift", "ctrl", "alt", "control"];

export function brushSelecct(G6) {
  G6.registerBehavior("brush-select", {
    getDefaultCfg() {
      return {
        brushStyle: {
          fill: "#EEF6FF",
          fillOpacity: 0.4,
          stroke: "#DDEEFE",
          lineWidth: 1,
        },
        onSelect() {},
        onDeselect() {},
        selectedState: "selected",
        trigger: DEFAULT_TRIGGER,
        includeEdges: true,
        selectedEdges: [],
        selectedNodes: [],
      };
    },
    getEvents() {
      let trigger;
      // 检测输入是否合法
      if (ALLOW_EVENTS.indexOf(this.trigger.toLowerCase()) > -1) {
        trigger = this.trigger;
      } else {
        trigger = DEFAULT_TRIGGER;
        console.warn("Behavior brush-select的trigger参数不合法，请输入drag、shift、ctrl或alt");
      }
      if (trigger === "drag") {
        return {
          mousedown: "onMouseDown",
          mousemove: "onMouseMove",
          mouseup: "onMouseUp",
          "canvas:click": "clearStates",
        };
      }
      return {
        mousedown: "onMouseDown",
        mousemove: "onMouseMove",
        mouseup: "onMouseUp",
        "canvas:click": "clearStates",
        keyup: "onKeyUp",
        keydown: "onKeyDown",
      };
    },
    onMouseDown(e) {
      // 按在node上面拖动时候不应该是框选
      const { item } = e;
      if (item) {
        return;
      }

      if (this.trigger !== "drag" && !this.keydown) {
        return;
      }

      if (this.selectedNodes && this.selectedNodes.length !== 0) {
        this.clearStates();
      }

      let brush = this.brush;
      if (!brush) {
        brush = this._createBrush();
      }
      this.originPoint = { x: e.canvasX, y: e.canvasY };
      brush.attr({ width: 0, height: 0 });
      brush.show();
      this.dragging = true;
    },
    onMouseMove(e) {
      if (!this.dragging) {
        return;
      }

      if (this.trigger !== "drag" && !this.keydown) {
        return;
      }

      this._updateBrush(e);
      this.graph.paint();
    },
    onMouseUp(e) {
      if (!this.brush && !this.dragging) {
        return;
      }

      if (this.trigger !== "drag" && !this.keydown) {
        return;
      }

      const graph = this.graph;
      const autoPaint = graph.get("autoPaint");
      graph.setAutoPaint(false);
      this.brush.destroy();
      this.brush = null;
      this._getSelectedNodes(e);
      this.dragging = false;
      this.graph.paint();
      graph.setAutoPaint(autoPaint);
    },
    clearStates() {
      if (this.keydown) {
        return;
      }

      const graph = this.graph;
      const autoPaint = graph.get("autoPaint");
      graph.setAutoPaint(false);
      const selectedState = this.selectedState;

      const nodes = graph.findAllByState("node", selectedState);
      const edges = graph.findAllByState("edge", selectedState);
      nodes.forEach((node) => graph.setItemState(node, selectedState, false));
      edges.forEach((edge) => graph.setItemState(edge, selectedState, false));

      this.selectedNodes = [];

      this.selectedEdges = [];
      this.onDeselect && this.onDeselect(this.selectedNodes, this.selectedEdges);
      graph.emit("nodeselectchange", {
        targets: {
          nodes: [],
          edges: [],
        },
        select: false,
      });
      graph.paint();
      graph.setAutoPaint(autoPaint);
    },
    _getSelectedNodes(e) {
      const graph = this.graph;
      const state = this.selectedState;
      const originPoint = this.originPoint;
      const p1 = { x: e.x, y: e.y };
      const p2 = graph.getPointByCanvas(originPoint.x, originPoint.y);
      const left = min(p1.x, p2.x);
      const right = max(p1.x, p2.x);
      const top = min(p1.y, p2.y);
      const bottom = max(p1.y, p2.y);
      const selectedNodes = [];
      const shouldUpdate = this.shouldUpdate;
      const selectedIds = [];
      graph.getNodes().forEach((node) => {
        const bbox = node.getBBox();
        if (bbox.centerX >= left && bbox.centerX <= right && bbox.centerY >= top && bbox.centerY <= bottom) {
          if (shouldUpdate(node, "select")) {
            selectedNodes.push(node);
            const model = node.getModel();
            selectedIds.push(model.id);
            graph.setItemState(node, state, true);
          }
        }
      });

      const selectedEdges = [];
      if (this.includeEdges) {
        // 选中边，边的source和target都在选中的节点中时才选中
        selectedNodes.forEach((node) => {
          const edges = node.getEdges();
          edges.forEach((edge) => {
            const model = edge.getModel();
            const { source, target } = model;
            if (selectedIds.includes(source) && selectedIds.includes(target) && shouldUpdate(edge, "select")) {
              selectedEdges.push(edge);
              graph.setItemState(edge, this.selectedState, true);
            }
          });
        });
      }

      this.selectedEdges = selectedEdges;
      this.selectedNodes = selectedNodes;
      this.onSelect && this.onSelect(selectedNodes, selectedEdges);
      graph.emit("nodeselectchange", {
        targets: {
          nodes: selectedNodes,
          edges: selectedEdges,
        },
        select: true,
      });
    },
    _createBrush() {
      const self = this;
      const brush = self.graph.get("canvas").addShape("rect", {
        attrs: self.brushStyle,
        capture: false,
      });
      this.brush = brush;
      return brush;
    },
    _updateBrush(e) {
      const originPoint = this.originPoint;
      this.brush.attr({
        width: abs(e.canvasX - originPoint.x),
        height: abs(e.canvasY - originPoint.y),
        x: min(e.canvasX, originPoint.x),
        y: min(e.canvasY, originPoint.y),
      });
    },
    onKeyDown(e) {
      const code = e.key;
      // 按住control键时，允许用户设置trigger为ctrl
      if ((code && code.toLowerCase() === this.trigger.toLowerCase()) || code.toLowerCase() === "control") {
        this.keydown = true;
      } else {
        this.keydown = false;
      }
    },
    onKeyUp() {
      if (this.brush) {
        // 清除所有选中状态后，设置拖得动状态为false，并清除框选的brush
        this.brush.destroy();
        this.brush = null;
        this.dragging = false;
      }
      this.keydown = false;
    },
  });
}
